package com.me.study.concurrent.thread;

/**
 * 测试多线程环境下 synchronized 关键字<p></p>
 *
 * 1，多个线程调用的不同实例的同步方法，线程不互斥<p>
 * 2，PrivateNumB.test2()加 synchronized 时，无论断点打在 sleep() 之前还是之后，结果一样，说明【两个线程调用两个不同的同步方法，因为锁对象一致，两个线程同步执行】
 *      <blockquote>
 *      线程[1]已经设置完毕<p>
 *      线程[1]的值：100<p>
 *      线程[2]执行，线程[1]并没有同步执行</blockquote>
 * <p>
 * 3，PrivateNumB.test2()不加 synchronized 时，【两个线程分别调用同步方法和非同步方法，线程不会同步执行】
 * <p>PS: 一个实例的两个方法，一个修改（synchronized），一个读（非synchronized），A 没修改完，B 可能读到修改之前的值
 * <ul>
 * <li>断点打在 sleep() 之前：<blockquote>
 *      线程[2]执行，线程[1]并没有同步执行<p>
 *      线程[1]已经设置完毕<p>
 *      线程[1]的值：100</blockquote>
 * <li>断点打在 sleep() 之后：<blockquote>
 *      线程[1]已经设置完毕<p>
 *      线程[2]执行，线程[1]并没有同步执行<p>
 *      线程[1]的值：100</blockquote>
 * </ul>
 *
 * @author ME
 * @date 2019/1/15 11:19
 */
public class TestSynchronized {
    public static void main(String[] args) {
        PrivateNumB p1 = new PrivateNumB();

        // t1 执行 test1，t2 执行 test2
        MyThreadB t1 = new MyThreadB('1', p1);
        MyThreadB2 t2 = new MyThreadB2('2', p1);
        try {
            // 必须在同步代码块中调用，因为会先检查锁，没有则会 java.lang.IllegalMonitorStateException
            t1.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.start();
        t2.start();
    }

    public static class PrivateNumB {
        int num = 0;

        public synchronized void test(char i) {
            try {
                if (i == '1') {
                    num = 100;

                    System.out.println("线程[1]已经设置完毕");
                    Thread.sleep(1000);
                } else {
                    num = 200;
                    System.out.println("线程[2]已经设置完毕");
                }
                System.out.println("线程[" + i + "]的值：" + num);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public  void test2(char i) {
            System.out.println("线程[" + i + "]执行，线程[1]并没有同步执行");
        }
    }

    public static class MyThreadB extends Thread {
        char i;
        PrivateNumB p;

        public MyThreadB(char i, PrivateNumB p) {
            this.i = i;
            this.p = p;
        }

        @Override
        public void run() {
            p.test(i);
        }
    }

    public static class MyThreadB2 extends Thread {
        char i;
        PrivateNumB p;

        public MyThreadB2(char i, PrivateNumB p) {
            this.i = i;
            this.p = p;
        }

        @Override
        public void run() {
            p.test2(i);
        }
    }
}
